package zz.demo.springCloud.filter;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import zz.demo.springCloud.adapter.RateLimiterAdapter;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by zhangzheng on 2017-6-14.
 * <p>
 * 此为serviceFilter，即根据serviceId来进行限流，如果需要根据用户或者URL来限流，再写一个即可 。
 */
@Component
public class ServiceFilter extends ZuulFilter {
    private Map<String, RateLimiterAdapter> rateLimiterMap = Maps.newConcurrentMap();
    private static final String SERVICE_ID = "serviceId";
    private static final String TIME_KEY = "_TIME";
    private static final String COUNT_KEY = "_COUNT";


    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    // oderFilter顺序必须大于5，即大于org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter的order，否则获取不到serviceId
    @Override
    public int filterOrder() {
        return 100;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        String serviceId = (String) currentContext.get(SERVICE_ID);
        System.out.println("访问的service："+serviceId);

        if (isRedis()) {
            // 或是在redis中存储相关限流参数
            if(!redisTemplate.hasKey(serviceId+TIME_KEY)){
                redisTemplate.opsForValue().set(serviceId+TIME_KEY,0,60,TimeUnit.SECONDS);
                redisTemplate.delete(serviceId + COUNT_KEY);
            }
            if(redisTemplate.hasKey(serviceId+TIME_KEY) && redisTemplate.opsForValue().increment
                    (serviceId+COUNT_KEY,1)<1000){
                return null ;
            }
        } else {
            RateLimiterAdapter rateLimiterAdapter = null;
            if (serviceId != null) {
                if (!rateLimiterMap.containsKey(serviceId)) {
                    rateLimiterAdapter = new RateLimiterAdapter(RateLimiterAdapter.RateLimiterAdapterHolder.UnBlockChannel,
                            RateLimiter.create(RateLimiterAdapter.RateLimiterAdapterHolder.Max));
                } else {
                    rateLimiterAdapter = rateLimiterMap.get(serviceId);
                }
            } else {
                // TODO
                // serviceId为空的情况，应去数据库、redis中查找对应的静态数据表（即如果更新可更新到此表上）。
            }
            if (rateLimiterAdapter != null) {
                if (rateLimiterAdapter.doLimited()) {
                    return null;
                }
            }
        }
        try {
            HttpStatus httpStatus = HttpStatus.TOO_MANY_REQUESTS;
            HttpServletResponse response = currentContext.getResponse();
            response.setContentType(MediaType.TEXT_PLAIN_VALUE);
            response.getWriter().append(httpStatus.getReasonPhrase());

            currentContext.setSendZuulResponse(false);
            throw new ZuulException(
                    httpStatus.getReasonPhrase(),
                    httpStatus.value(),
                    httpStatus.getReasonPhrase()
            );
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ZuulException e) {
            ReflectionUtils.rethrowRuntimeException(e);
        }
        return null;
    }

    public boolean isRedis() {
        return true;
    }


}
